const std = @import("std");
const builtin = @import("builtin");
const kernel = @import("kernel");
const log = std.log.scoped(.@"test");

pub const os = kernel.os;
pub const panic = kernel.panic;
pub const std_options = kernel.std_options;

pub fn tests() noreturn {
    @disableInstrumentation();

    var passed: u64 = 0;
    var skipped: u64 = 0;
    var failed: u64 = 0;

    const p = os.proc.current().?;
    for (builtin.test_functions) |*test_fn| {
        _ = p.spawn(test_fn.name, .{ .func = &thread, .arg = test_fn });
    }

    var status: i32 = 0;
    while (p.wait(&status)) |_| {
        switch (status) {
            0 => passed += 1,
            1 => skipped += 1,
            2 => failed += 1,
            else => unreachable,
        }
    } else |_| {}

    log.info("{} passed, {} skipped, {} failed", .{ passed, skipped, failed });
    os.proc.shutdown(@truncate(failed));
}

/// Worker thread for individual test execution
fn thread(arg: ?*const anyopaque) void {
    const test_fn: *const std.builtin.TestFn = @ptrCast(@alignCast(arg.?));
    const name = test_fn.name;

    var status: i32 = 0;
    if (test_fn.func()) |_| {
        status = 0;
        log.info("{s}... PASS", .{name});
    } else |err| if (err == error.SkipZigTest) {
        status = 1;
        log.warn("{s}... SKIP", .{name});
    } else {
        status = 2;
        log.err("{s}... FAIL", .{name});
    }

    os.proc.current().?.exit(status);
}
